package excel

import (
	"fmt"
	"os"
	"strings"

	"github.com/sniperHW/kendynet"
)

const (
	TYPE_int32    = 1 << 1
	TYPE_string   = 1 << 2
	TYPE_float    = 1 << 3
	TYPE_bool     = 1 << 4 //add
	TYPE_struct   = 1 << 5
	TYPE_array    = 1 << 6
	TYPE_addarray = 1 << 7 //add
)

func TypeToString(tt int) string {
	if tt == TYPE_int32 {
		return "int32"
	} else if tt == TYPE_float {
		return "float64"
	} else if tt == TYPE_string {
		return "string"
	} else if tt == TYPE_bool {
		return "bool"
	} else if tt == TYPE_array {
		return "array"
	} else if tt == TYPE_struct {
		return "struct"
	} else if tt == TYPE_addarray {
		return "addarray"
	} else {
		return ""
	}
}

type EleType struct {
	Name   string
	Type   int
	IsKey  bool
	Fields []EleType
	Sep    string
}

type TabDef struct {
	Name string
	Cols []EleType //需要的列
}

func Split(value, sep string) [][]string {
	ret := make([][]string, 0)
	if len(sep) > 1 {

		sep1 := string([]byte(sep)[:1])
		sep2 := string([]byte(sep)[1:])
		r := strings.Split(value, sep1)
		for _, v := range r {
			if v != "" {
				ret = append(ret, strings.Split(v, sep2))
			}
		}
	} else {
		ret = append(ret, strings.Split(value, sep))
	}

	return ret
}

//add
func RowSplit(value string) [][]string {
	ret := make([][]string, 0)
	sep1 := strings.Split(value, ";")
	for _, v := range sep1 {
		ret = append(ret, strings.Split(v, ","))
	}
	return ret
}

var Tabs map[string]TabDef

func addSubField(tab *TabDef, e *EleType, field string) {
	t := strings.Split(field, ":")
	if len(t) != 2 {
		fmt.Printf(" %s 非法列定义\n", tab.Name)
		os.Exit(0)
	}

	subField := EleType{
		Name: t[0],
	}

	if t[1] == "int32" {
		subField.Type = TYPE_int32
	} else if t[1] == "string" {
		subField.Type = TYPE_string
	} else if t[1] == "float" {
		subField.Type = TYPE_float
	} else if t[1] == "bool" {
		subField.Type = TYPE_bool
	} else {
		fmt.Printf(" %s 非法列定义\n", tab.Name)
		os.Exit(0)
	}

	e.Fields = append(e.Fields, subField)

}

func processStruct(tab *TabDef, e *EleType, fields string, sep string) {

	if len(sep) == 0 {
		fmt.Printf("%s 非法分割符定义\n", tab.Name)
		os.Exit(0)
	}

	e.Type |= TYPE_struct
	e.Sep = sep

	t1 := strings.Split(fields, ",")

	if len(t1) == 0 {
		fmt.Printf(" %s 非法列定义\n", tab.Name)
		os.Exit(0)
	}

	for _, v := range t1 {
		addSubField(tab, e, v)
	}
}

func processArray(tab *TabDef, e *EleType, fields string, sep string) {

	if len(sep) == 0 {
		fmt.Printf("%s 非法分割符定义\n", tab.Name)
		os.Exit(0)
	}

	e.Type |= TYPE_array
	e.Sep = sep

	if fields == "int32" || fields == "string" || fields == "float" || fields == "bool" {
		if fields == "int32" {
			e.Type = e.Type | TYPE_int32
		} else if fields == "string" {
			e.Type = e.Type | TYPE_string
		} else if fields == "bool" {
			e.Type = e.Type | TYPE_bool
		} else {
			e.Type = e.Type | TYPE_float
		}
	} else {
		processStruct(tab, e, fields, sep)
	}
}

//add
func processAddArray(tab *TabDef, e *EleType, fields string, sep string) {

	if len(sep) == 0 {
		fmt.Printf("%s 非法分割符定义\n", tab.Name)
		os.Exit(0)
	}

	e.Type |= TYPE_addarray
	e.Sep = sep

	if fields == "int32" || fields == "string" || fields == "float" || fields == "bool" {
		if fields == "int32" {
			e.Type = e.Type | TYPE_int32
		} else if fields == "string" {
			e.Type = e.Type | TYPE_string
		} else if fields == "bool" {
			e.Type = e.Type | TYPE_bool
		} else {
			e.Type = e.Type | TYPE_float
		}
	} else {
		processStruct(tab, e, fields, sep)
	}
}

func processCol(tab *TabDef, colDef string) {

	e := EleType{}

	//将定义分割成4部分，名字，类型，拆分串,分割符
	/**add**/ //将定义分割成4部分，名字，类型，拆分串,读取列串
	t1 := strings.Split(colDef, "@")
	if len(t1) < 2 {
		fmt.Printf(" %s 非法列定义 %s \n", tab.Name, colDef)
		os.Exit(0)
	}

	t2 := strings.Split(t1[0], ",")

	e.Name = t2[0]
	if len(t2) > 1 && t2[1] == "key" {
		e.IsKey = true
	}

	tt := t1[1]

	if tt == "int32" {
		e.Type |= TYPE_int32
	} else if tt == "float" {
		e.Type |= TYPE_float
	} else if tt == "string" {
		e.Type |= TYPE_string
	} else if tt == "bool" {
		e.Type |= TYPE_bool
	} else if tt == "array" {
		if len(t1) != 4 {
			fmt.Printf(" %s 非法列定义 %s \n", tab.Name, colDef)
			os.Exit(0)
		}
		processArray(tab, &e, t1[2], t1[3])
	} else if tt == "struct" {
		if len(t1) != 4 {
			fmt.Printf(" %s 非法列定义 %s \n", tab.Name, colDef)
			os.Exit(0)
		}
		processStruct(tab, &e, t1[2], t1[3])
	} else if tt == "addarray" { //add
		if len(t1) != 4 {
			fmt.Printf(" %s 非法列定义 %s \n", tab.Name, colDef)
			os.Exit(0)
		}
		processAddArray(tab, &e, t1[2], t1[3])
	} else {
		fmt.Printf(" %s 非法列定义类型 %s \n", tab.Name, colDef)
		os.Exit(0)
	}

	tab.Cols = append(tab.Cols, e)
}

func AddSheet(sheetName string, colDef ...string) {
	if _, ok := Tabs[sheetName]; ok {
		fmt.Printf(" %s 重复定义\n", sheetName)
		os.Exit(0)
	}

	if len(colDef) == 0 {
		fmt.Printf(" %s 没有列定义\n", sheetName)
		os.Exit(0)
	}

	tab := TabDef{
		Name: sheetName,
		Cols: make([]EleType, 0),
	}

	for _, v := range colDef {
		processCol(&tab, v)
	}

	Tabs[sheetName] = tab
}

func EmptyFunc() {

}

func gen_field(e EleType, f *os.File) {
	if e.Type&TYPE_struct > 0 {
		out := fmt.Sprintf(
			`
type %s struct {
`, (strings.Title(e.Name) + "_"))

		for _, v := range e.Fields {
			var typeString string
			if v.Type&TYPE_int32 > 0 {
				typeString += "int32"
			} else if v.Type&TYPE_float > 0 {
				typeString += "float64"
			} else if v.Type&TYPE_string > 0 {
				typeString += "string"
			} else if v.Type&TYPE_bool > 0 {
				typeString += "bool"
			}
			out += ("	" + strings.Title(v.Name) + " " + typeString + "\n")
		}
		out = out + "}\n"

		f.WriteString(out)

	}
}

func gen_tab(tab TabDef) {

	os.MkdirAll("table/"+tab.Name, os.ModePerm)
	out_path := fmt.Sprintf("table/%s/%s.go", tab.Name, tab.Name)

	f, err := os.OpenFile(out_path, os.O_RDWR, os.ModePerm)
	if err != nil {
		if os.IsNotExist(err) {
			f, err = os.Create(out_path)
			if err != nil {
				kendynet.Errorf("create %s failed:%s", out_path, err.Error())
				return
			}
		} else {
			kendynet.Errorf("open %s failed:%s", out_path, err.Error())
			return
		}
	}

	os.Truncate(out_path, 0)

	f.WriteString(fmt.Sprintf(
		`
package %s

import(
	"fmt"
	"sanguo/node/table/excel"
	"sanguo/node/table/excel/table"
	"strconv"

	"github.com/tealeg/xlsx"
)

func readInt32(v string) int32 {
	return int32(readFloat(v))
}

func readFloat(v string) float64 {
	vv,err := strconv.ParseFloat(v,64)
	if nil != err {
		panic(err)
	}
	return vv
}

func readBool(str string) bool {
    b, err := strconv.ParseBool(str)
	if nil != err {
		panic(err)
	}
    return b
}
`, tab.Name))

	f.WriteString(`
func readStr(v string) string {
	return fmt.Sprintf("%s",v)
}
`)

	for _, v := range tab.Cols {
		gen_field(v, f)
	}
	out := fmt.Sprintf(
		`
type %s struct {
`, strings.Title(tab.Name))

	for _, v := range tab.Cols {

		var typeString string

		if v.Type&TYPE_array > 0 {
			typeString = "[]"
		}
		//add
		if v.Type&TYPE_addarray > 0 {
			typeString = "[]"
		}

		if v.Type&TYPE_int32 > 0 {
			typeString += "int32"
		} else if v.Type&TYPE_float > 0 {
			typeString += "float64"
		} else if v.Type&TYPE_string > 0 {
			typeString += "string"
		} else if v.Type&TYPE_bool > 0 {
			typeString += "bool"
		} else if v.Type&TYPE_struct > 0 {
			typeString += (strings.Title(v.Name) + "_")
		}

		out += ("	" + strings.Title(v.Name) + " " + typeString + "\n")
	}
	out = out + "}\n"

	f.WriteString(out)

	gen_loader(&tab, f)

	f.Close()
}

func gen_compound_reader(e *EleType, f *os.File) {
	//add 只会是结构体
	if e.Type&TYPE_addarray > 0 {
		retStr := e.Name
		retTypeStr := strings.Title(e.Name) + "_"
		rowStr := e.Name + "_"
		template := `
func readAdd%s(names, row *xlsx.Row) []%s {
			
	%s := make([]%s,0)
	r := excel.RowSplit("%s")
	_, err := strconv.ParseFloat(r[0][0], 64)
	
	if err == nil {		
		for _,v := range(r) {
			%s
		}
	} else {
		for i := readInt32(r[1][0]); i <= readInt32(r[1][1]); i++ {
			%s
		}
	}
	return %s
}
`

		str1 := ""
		str2 := ""

		str1 += fmt.Sprintf("%s := %s{}\n", rowStr, retTypeStr)
		str2 += fmt.Sprintf("%s := %s{}\n", rowStr, retTypeStr)

		for k, v := range e.Fields {
			if v.Type&TYPE_int32 > 0 {

				str1 += fmt.Sprintf(
					`
			%s.%s = readInt32(row.Cells[readInt32(v[%d])].String())
				`, rowStr, strings.Title(v.Name), k)

				str2 += fmt.Sprintf(`
			%s.%s = readInt32(row.Cells[getIdxByName(names, fmt.Sprintf(`, rowStr, strings.Title(v.Name))
				str2 += `"%s%d"` + fmt.Sprintf(`, r[0][%d], i))].String())
				`, k)

			} else if v.Type&TYPE_float > 0 {
				str1 += fmt.Sprintf(
					`
			%s.%s = readFloat(row.Cells[readInt32(v[%d])].String())
				`, rowStr, strings.Title(v.Name), k)

				str2 += fmt.Sprintf(`
			%s.%s = readFloat(row.Cells[getIdxByName(names, fmt.Sprintf(`, rowStr, strings.Title(v.Name))
				str2 += `"%s%d"` + fmt.Sprintf(`, r[0][%d], i))].String())
				`, k)

			} else if v.Type&TYPE_bool > 0 {
				str1 += fmt.Sprintf(
					`
			%s.%s = readBool(row.Cells[readInt32(v[%d])].String())
				`, rowStr, strings.Title(v.Name), k)

				str2 += fmt.Sprintf(`
			%s.%s = readBool(row.Cells[getIdxByName(names, fmt.Sprintf(`, rowStr, strings.Title(v.Name))
				str2 += `"%s%d"` + fmt.Sprintf(`, r[0][%d], i))].String())
				`, k)

			} else if v.Type&TYPE_string > 0 {
				str1 += fmt.Sprintf(
					`
			%s.%s = readStr(row.Cells[readInt32(v[%d])].String())
				`, rowStr, strings.Title(v.Name), k)

				str2 += fmt.Sprintf(`
			%s.%s = readStr(row.Cells[getIdxByName(names, fmt.Sprintf(`, rowStr, strings.Title(v.Name))
				str2 += `"%s%d"` + fmt.Sprintf(`, r[0][%d], i))].String())
				`, k)
			}

		}

		str1 += fmt.Sprintf(`
			%s = append(%s,%s)`, retStr, retStr, rowStr)
		str2 += fmt.Sprintf(`
			%s = append(%s,%s)`, retStr, retStr, rowStr)

		f.WriteString(fmt.Sprintf(template, strings.Title(e.Name), retTypeStr, retStr, retTypeStr, e.Sep, str1, str2, retStr))

		return
	}

	if e.Type&TYPE_array > 0 {
		if e.Type&TYPE_struct == 0 {
			//简单类型数组
			template := `
func read%s(value string) []%s {
	ret := make([]%s,0)
	r := excel.Split(value,";")
	for _,v := range(r[0]) {
		ret = append(ret,%s)	
	}
	return ret
}		 	
`
			if e.Type&TYPE_int32 > 0 {
				f.WriteString(fmt.Sprintf(template, strings.Title(e.Name), "int32", "int32", "readInt32(v)"))
			} else if e.Type&TYPE_float > 0 {
				f.WriteString(fmt.Sprintf(template, strings.Title(e.Name), "float64", "float64", "readFloat(v)"))
			} else if e.Type&TYPE_string > 0 {
				f.WriteString(fmt.Sprintf(template, strings.Title(e.Name), "string", "string", "readStr(v)"))
			} else if e.Type&TYPE_bool > 0 {
				f.WriteString(fmt.Sprintf(template, strings.Title(e.Name), "bool", "bool", "readBool(v)"))
			}

		} else {
			//复合类型数组
			retStr := e.Name
			retTypeStr := strings.Title(e.Name) + "_"
			rowStr := e.Name + "_"
			f.WriteString(fmt.Sprintf(`
func read%s(value string) []%s {

	%s := make([]%s,0)
	r := excel.Split(value,"%s")

	for _,v := range(r) {
		%s := %s{}
`, strings.Title(e.Name), retTypeStr, retStr, retTypeStr, e.Sep, rowStr, retTypeStr))

			for k, v := range e.Fields {
				if v.Type&TYPE_int32 > 0 {

					f.WriteString(fmt.Sprintf(
						`
		%s.%s = readInt32(v[%d])
`, rowStr, strings.Title(v.Name), k))

				} else if v.Type&TYPE_float > 0 {
					f.WriteString(fmt.Sprintf(
						`
		%s.%s = readFloat(v[%d])
`, rowStr, strings.Title(v.Name), k))

				} else if v.Type&TYPE_bool > 0 {
					f.WriteString(fmt.Sprintf(
						`
		%s.%s = readBool(v[%d])
`, rowStr, strings.Title(v.Name), k))

				} else if v.Type&TYPE_string > 0 {
					f.WriteString(fmt.Sprintf(
						`
		%s.%s = readStr(v[%d])
`, rowStr, strings.Title(v.Name), k))
				}

			}

			f.WriteString(fmt.Sprintf(`	
		%s = append(%s,%s)
	}	 	
	return %s
}
`, retStr, retStr, rowStr, retStr))
		}
		return
	}

	if e.Type&TYPE_struct > 0 {
		retStr := e.Name
		retTypeStr := strings.Title(e.Name) + "_"
		f.WriteString(fmt.Sprintf(`
func read%s(value string) %s {

	%s := %s{}
	r := excel.Split(value,"%s")
`, strings.Title(e.Name), retTypeStr, retStr, retTypeStr, e.Sep))

		for k, v := range e.Fields {
			if v.Type&TYPE_int32 > 0 {

				f.WriteString(fmt.Sprintf(
					`
	%s.%s = readInt32(r[0][%d])
`, retStr, strings.Title(v.Name), k))

			} else if v.Type&TYPE_float > 0 {
				f.WriteString(fmt.Sprintf(
					`
	%s.%s = readFloat(r[0][%d])
`, retStr, strings.Title(v.Name), k))

			} else if v.Type&TYPE_bool > 0 {
				f.WriteString(fmt.Sprintf(
					`
	%s.%s = readBool(r[0][%d])
`, retStr, strings.Title(v.Name), k))

			} else if v.Type&TYPE_string > 0 {
				f.WriteString(fmt.Sprintf(
					`
	%s.%s = readStr(r[0][%d])
`, retStr, strings.Title(v.Name), k))
			}

		}

		f.WriteString(fmt.Sprintf(`		
	return %s
}
`, retStr))

	}

}

func gen_init(tab *TabDef, f *os.File) {

	template := `
type Table struct {
%s	xlsxName string
}

var Table_ Table

func getIdxByName(names *xlsx.Row, name string) int {
	for i, v := range names.Cells {
		if v.String() == name {
			return i
		}
	}
	return -1
}

func init() {
	Table_.xlsxName = "%s.xlsx"
%s
	table.AddTable(&Table_)
}
`

	mapDef := ""
	mapInit := ""

	for _, v := range tab.Cols {
		if v.IsKey {
			mapDef += fmt.Sprintf("	%sMap map[%s]*%s\n", strings.Title(v.Name), TypeToString(v.Type), strings.Title(tab.Name))
			mapDef += fmt.Sprintf("	tmp%sMap map[%s]*%s\n", strings.Title(v.Name), TypeToString(v.Type), strings.Title(tab.Name))
			mapInit += fmt.Sprintf("	Table_.%sMap = make(map[%s]*%s)\n", strings.Title(v.Name), TypeToString(v.Type), strings.Title(tab.Name))
			mapInit += fmt.Sprintf("	Table_.tmp%sMap = make(map[%s]*%s)\n", strings.Title(v.Name), TypeToString(v.Type), strings.Title(tab.Name))

		}
	}

	f.WriteString(fmt.Sprintf(template, mapDef, tab.Name, mapInit))

}

func gen_Load(tab *TabDef, f *os.File) {

	template := `

func (this *Table) Load(path string) {

	excel.EmptyFunc()

    xlFile, err := xlsx.OpenFile(path + this.xlsxName)
    if err != nil {
		panic(err)
    }

    sheet := xlFile.Sheets[0]
    names := sheet.Rows[1]
    rows  := sheet.Rows[3:]

    for _,row := range(rows) {
    	if len(row.Cells) > 0 && row.Cells[0].String() != "" {
    		ele := &%s{}
    	
%s
		}
    }

    %s 
}

`

	eleLoadStr := ""
	mapStr := ""

	for _, v := range tab.Cols {

		//add
		if v.Type&TYPE_addarray > 0 {
			eleLoadStr += fmt.Sprintf(`		ele.%s = readAdd%s(names, row)
`, strings.Title(v.Name), strings.Title(v.Name))
		} else {
			if v.Type&TYPE_struct > 0 || v.Type&TYPE_array > 0 {
				eleLoadStr += fmt.Sprintf(`		ele.%s = read%s(row.Cells[getIdxByName(names, "%s")].String())
`, strings.Title(v.Name), strings.Title(v.Name), v.Name)
			} else {
				if v.Type&TYPE_int32 > 0 {
					eleLoadStr += fmt.Sprintf(`		ele.%s = readInt32(row.Cells[getIdxByName(names, "%s")].String())
`, strings.Title(v.Name), v.Name)
				} else if v.Type&TYPE_float > 0 {
					eleLoadStr += fmt.Sprintf(`		ele.%s = readFloat(row.Cells[getIdxByName(names, "%s")].String())
`, strings.Title(v.Name), v.Name)
				} else if v.Type&TYPE_string > 0 {
					eleLoadStr += fmt.Sprintf(`		ele.%s = readStr(row.Cells[getIdxByName(names, "%s")].String())
`, strings.Title(v.Name), v.Name)
				} else if v.Type&TYPE_bool > 0 {
					eleLoadStr += fmt.Sprintf(`		ele.%s = readBool(row.Cells[getIdxByName(names, "%s")].String())
`, strings.Title(v.Name), v.Name)
				}
			}
		}
		if v.IsKey {
			eleLoadStr += fmt.Sprintf("		this.tmp%sMap[ele.%s] = ele\n", strings.Title(v.Name), strings.Title(v.Name))
		}
	}

	for _, v := range tab.Cols {
		if v.IsKey {
			mapStr += fmt.Sprintf("this.%sMap = this.tmp%sMap\n", strings.Title(v.Name), strings.Title(v.Name))
		}
	}

	f.WriteString(fmt.Sprintf(template, strings.Title(tab.Name), eleLoadStr, mapStr))

}

func gen_loader(tab *TabDef, f *os.File) {
	for _, v := range tab.Cols {
		if v.Type&TYPE_array > 0 || v.Type&TYPE_struct > 0 || v.Type&TYPE_addarray > 0 {
			gen_compound_reader(&v, f)
		}
	}
	gen_init(tab, f)
	gen_Load(tab, f)
}

func Gen() {
	os.MkdirAll("table", os.ModePerm)
	for _, v := range Tabs {
		gen_tab(v)
	}
}
